#include <QTextCodec>
#include <QString>
#include <QStringList>
#include <QList>
#include <QStack>
#include <QDateTime>
#include <QLocale>

#include "emailmessage.h"

const QString constTextPlain = "text/plain";
const QString constTextHtml  = "text/html";
const QString constRfc822    = "message/rfc822";
const QString constMultipart = "multipart";
const QString constBoundary  = "boundary";
const QString constCharset   = "charset";

/// Function HexCode
/// Return decimal code of a hex number
unsigned char HexCode( const char ch )
{
	if ( ch > 47 && ch < 58 )
	{
		return ( ch - 48 );
	}
    else if ( ch > 64 && ch < 71 )
	{
		return ( ch - 55 );
	}
    else
    {
        return 0;
    }
}


const QString& EmailMessage::getMessageId() const
{
	return messageId_;
}

const QStringList& EmailMessage::getTo() const
{
	return recipients_;
}

const QString& EmailMessage::getFrom() const
{
	return sender_;
}

const QStringList& EmailMessage::getCc() const
{
	return cc_;
}

const QStringList& EmailMessage::getBcc() const
{
	return bcc_;
}


const QString& EmailMessage::getSubject() const
{
	return subject_;
}

QDateTime EmailMessage::getDate() const
{
	return date_;
}

uint EmailMessage::getPriority() const
{
	return priority_;
}

void EmailMessage::clear()
{
	messageId_ = "";
	recipients_.clear();
	cc_.clear();
	bcc_.clear();
	sender_ = "";
	subject_ = "";
	priority_ = 3;
}

///
///	EmailMessage helps with parsing raw bytes 
/// email messages, and extracting all necessary information
///

EmailMessage::EmailMessage()
{
	bParseHeaderOnly_ = false;
	init();
}

EmailMessage::~EmailMessage()
{		
	if ( bAutoDelete_ )
	{
		emailStream_->close();
		delete emailStream_;
	}
}

///
///	@return true if message parsed successfully, and false otherwise
///
bool EmailMessage::open(QIODevice *stream)
{
	// Initialize
	init();
    Q_CHECK_PTR(stream);
    if (!stream->isOpen())
    {
        stream->open(QIODevice::ReadOnly);
    }
    stream->reset();
	emailStream_ = stream;

	bool bUnfolding = false;

	qint64 lastReadPos = 0;

    QString line;
    QString temp;
    temp = emailStream_->readLine();
    temp = temp.trimmed();

    if ( emailStream_->atEnd() )
	{
		// File is empty or have empty header
		return false;
	}

	// Parsing the Header
    while ( !emailStream_->atEnd() )
	{
        lastReadPos = emailStream_->pos();
        line = emailStream_->readLine();
        bUnfolding = false;
		if ( line.startsWith( ' ' ) || line.startsWith( '\t' ) )
		{
			bUnfolding = true;
		}

        line = line.trimmed();

        if ( line == boundary_ )
		{
			// Save boundary stream pointer for readAttachment fast use
			boundaryPos_ = lastReadPos;

			processParameter( temp );
			break;
		}

		// If first symbol is LWSP then it's continuation of previous string
		// otherwise parse the previous string
		if ( bUnfolding )
		{
			// Unfolding
            temp+=line;
		}
		else
		{
			processParameter( temp );
            //temp.assign( line.data(), line.size() );
            temp = line;
		}
	}

	// If attain end of file not lose last string
    if ( emailStream_->atEnd() )
	{
		processParameter( temp );
	}

	if ( !bParseHeaderOnly_ )
	{
		// Parsing the body
		return parseMimeBody();
	}

	return true;
}

///
///	@return mime type of a message
///
const QByteArray& EmailMessage::contentType()
{
	return retContentType_;
}

///
///	@return message text
///
const QString& EmailMessage::text()
{
	return text_;
}

///
/// @return email message size
///

size_t EmailMessage::size() const
{
	return emailStream_->size();
}

/// Attachments

/// 
/// @return true if email has attachments, otherwise false
/// 
bool EmailMessage::hasAttachments() const 
{
	return ( bool ) attachments_.size();
}

///
///  @return list of filenames of available attachments.
///
QStringList EmailMessage::attachments()
{
	QStringList attachs;

	for ( int i = 0; i < attachments_.size(); i++ )
	{
		attachs.append( attachments_[ i ].AttachmentName );
	}

	return attachs;
}

///
///	Reads attachment specified with file name
/// @param name - Attachment file name
/// @param container to place contents of an attachment or empty byte array if 
///			no such file exists.
///
bool EmailMessage::readAttachment(const QString& name, QByteArray& contents, QByteArray& contentType)
{
	contents.clear();

	// Find name in list
	qint64 offset = 0;
	for ( int i = 0; i < attachments_.size(); i++ )
	{
		if ( attachments_[ i ].AttachmentName == name )
		{
			offset = attachments_[ i ].FilePos;
			break;
		}
	}

	if ( !offset )
	{
		// Entry not found
		return false;
	}

    emailStream_->seek( offset );

    if ( emailStream_->atEnd() ) return false;

	// Buffers
    QString line;
    QString temp;
    QString strName;
    QString localBoundary;
    QString boundary;
    QString endBoundary;
	// Get the boundary

    boundary = emailStream_->readLine();
	// Check for UUEncoded message
	if ( boundary.startsWith( "begin " ) )
	{
        QByteArray filename;
		if ( isUUencoded( boundary, filename ) )
		{
			uudecode( true, contents );
		}
	}
	else
	{
        boundary = boundary.trimmed();
        endBoundary = boundary;
        endBoundary.append("--");

        //localBoundary.assign( "", 0 );
		bool bHeader = true;		// What is now parsing Header or Body
		bool bUnfolding = false;
		int nPos = 0;

        while( !emailStream_->atEnd() )
		{
			line.clear();
            line = emailStream_->readLine();

			// Check for the string continuation
			if ( line.startsWith( ' ' ) || line.startsWith( '\t' ) )
			{
				bUnfolding = true;
			}
			else
			{
				bUnfolding = false;
			}

            line = line.trimmed();

			if ( bHeader )
			{
				if ( bUnfolding && bHeader )
				{
					// Unfolding
                    temp.append(line);
				}
				else
				{
					// Start treat parameter
					if ( -1 != ( nPos = temp.indexOf( ':' ) ) )
					{
                        strName = temp.mid( 0, nPos ).trimmed();
						temp.remove( 0, nPos + 1 );
                        temp = temp.trimmed();

						// Multipart Content-Type
                        if ( strName.compare( "Content-Type",Qt::CaseInsensitive ) == 0 )
						{
                            contentType = temp.toLatin1();
						}
                        else if ( strName.compare( "Content-Transfer-Encoding",Qt::CaseInsensitive ) == 0 )
						{
							// Getting local Content-Transfer-Encoding
							if ( temp.startsWith( '"' ) && temp.endsWith( '"' ) )
							{
								temp.remove( 0, 1 );
								temp.remove( temp.size() - 1, 1 );
							}
                            encoding_ = temp.toLatin1();
						}
					} // end treat parameter

                    temp = line;
				}

				// Parse Header
                if ( line ==localBoundary )
				{
                    if ( localBoundary == boundary  )
					{
						temp.clear();
						localBoundary.clear();
						continue;
					}
					else
					{
						// Next is parsing of body
						bHeader = false;
					}
				}
				else
				{
                    if ( line ==endBoundary|| line == boundary )
					{
						break;
					} 
				}
			}
			else
			{
				// Parse Body
                if ( line == endBoundary || line == boundary )
				{
					break;
				}
				else
				{
                    contents.append(line);
					contents.append( "\r\n" );
				}
			} // Parsing of body
		} // while
        temp = encoding_;
		encode( contents, temp );
	}
	return true;
}

///
/// Says delete or not stream in destructor

void EmailMessage::setDeleteStream( bool isDelete )
{
	bAutoDelete_ = isDelete;
}

void EmailMessage::init()
{
	initParams();
    clear();
	text_ = "";
	attachments_.clear();
	boundaryPos_ = 0;
	bAddAttachment_ = false;
	attachName_ = "";
	attachPos_ = 0;
	bAutoDelete_ = false;
}

void EmailMessage::initParams()
{
	boundary_ = "";
	encoding_ = "";
	contentType_ = "";
	charset_ = "us-ascii";
	enumConType_ = e_text;		// Default Content-Type is text/plain
}

void EmailMessage::processParameter( QString &paramText )
{
    QString name;
	int nPos = 0;

	// Divide string onto param name and value and parse param
	if ( ( nPos = paramText.indexOf( ':' ) ) != -1 )
	{
        name = paramText.mid(  0,nPos ).trimmed();
		paramText.remove( 0, nPos + 1 );
        paramText = paramText.trimmed();

        if ( name.compare( "Content-Type",Qt::CaseInsensitive )  == 0)
		{
			parseContentType( paramText );
		}
        else if ( name.compare( "From",Qt::CaseInsensitive ) == 0 )
		{
			parseFrom( paramText );
		}
        else if ( name.compare( "To",Qt::CaseInsensitive )  == 0 )
		{
			parseAddrList( recipients_, paramText );
		}
        else if ( name.compare( "Message-Id" ,Qt::CaseInsensitive ) == 0 )
		{
			if ( paramText.startsWith( '<' ) && paramText.endsWith( '>' ) )
			{
				paramText.remove( 0, 1 );
				paramText.remove( paramText.size() - 1, 1 );
			}
            messageId_ = paramText;//messageId_ = paramText.data();
		}
        else if ( name.compare( "Subject" ,Qt::CaseInsensitive )  == 0)
		{
			subject_ = decodeNonAsciiText( paramText );
		}
        else if ( name.compare( "Content-Transfer-Encoding" ,Qt::CaseInsensitive ) == 0 )
		{
            encoding_ = paramText.toLatin1();//encoding_ = paramText.data();
		}
        else if ( name.compare( "Cc" ,Qt::CaseInsensitive ) == 0 )
		{
			parseAddrList( cc_, paramText );
		}
        else if ( name.compare( "Bcc" ,Qt::CaseInsensitive ) == 0 )
		{
			parseAddrList( bcc_, paramText );
		}
        else if ( name.compare( "X-Priority" ,Qt::CaseInsensitive ) == 0 )
		{
            priority_ = paramText.toInt();//priority_ = atoi( paramText.data() );
		}
        else if ( name.compare( "Date" ,Qt::CaseInsensitive ) == 0 )
		{
			parseDate( paramText );
		}
	}
}

//	============================================================
//	EmailMessage::parseMimeBody
///
/// Treat Content of the MIME file, beforehand have read the main
/// header and sets the enumConType_ member

bool EmailMessage::parseMimeBody()
{
	// Data declarations
	QStack< QByteArray > boundaryStack;
    QString line;
    QString boundary(boundary_);
    QString name;
    QString temp;
    QString localBoundary;
    QString endBoundary(boundary);
    endBoundary.append("--");
	QByteArray extractedText;
    QString charset(charset_);
    QString encoding(encoding_);
	bool bHeader = true;		// What is now parsing Header or Body
	bool bMulti = false;
	bool Result = true;
	bool bWasText = false, bWasHtml = false;
	bool bUnfolding = false;
	int nPos = 0;
	qint64 boundaryPos = boundaryPos_;
	int nameCounter = 0;
	retContentType_ = contentType_;

	if ( e_multipart == enumConType_ )
	{
        temp = emailStream_->readLine();
        temp = temp.trimmed();

        if ( emailStream_->atEnd() )
		{
			// No end boundary
			return false;
		}
        else if ( temp == endBoundary )
		{
			// End boundary just after start boundary
			return true;
		}

        while( !emailStream_->atEnd() )
		{
            qint64 curLinePos = emailStream_->pos();
            line = emailStream_->readLine();

			// Check for the string continuation
			if ( line.startsWith( ' ' ) || line.startsWith( '\t' ) )
			{
				bUnfolding = true;
			}
			else
			{
				bUnfolding = false;
			}

            line = line.trimmed();

			if ( bHeader )
			{
				if ( bUnfolding )
				{
					// Unfolding
                    temp.append( line );//temp.add( line );
				}
				else
				{
					// Process parameter
					if ( -1 != ( nPos = temp.indexOf( ':' ) ) )
					{
                        name = temp ;
                        name = name.mid( 0, nPos ).trimmed();
						temp.remove( 0, nPos + 1 );
                        temp = temp.trimmed();

						// Multipart Content-Type
                        if ( name.compare( "Content-Type",Qt::CaseInsensitive ) == 0 )
						{
							initParams();

							// The check on nested multipart content-types
							parseContentType( temp );

							if ( e_multipart == enumConType_ )
							{
								bHeader = true;
                                boundaryStack.push(  boundary.toLatin1() );
                                localBoundary = boundary_;
                                boundary = localBoundary ;
                                endBoundary = boundary ;
                                endBoundary.append( "--");
							}
							// We take only first text part (or first html if text not found)
							if ( temp.startsWith( constTextPlain ) || temp.startsWith( constRfc822 ) )
							{
								if ( bWasText || bWasHtml )
								{
									enumConType_ = e_other;
								}
								else
								{
									retContentType_ = contentType_;
                                    charset =  charset_;
									bWasText = true;
									extractedText.clear();
								}
							}
							else if ( temp.startsWith( constTextHtml ) )
							{
								if ( bWasHtml )
								{
									enumConType_ = e_other;
								}
								else
								{
									retContentType_ = contentType_;
                                    charset= charset_;
									bWasHtml = true;
									extractedText.clear();
								}
							}
						}
                        else if ( name.compare( "Content-Disposition",Qt::CaseInsensitive ) == 0 )
						{
							parseContentDisp( temp, bMulti, extractedText.isEmpty(),
								boundaryPos, nameCounter );
						}
						else if ( /*e_text == enumConType_ &&*/ extractedText.isEmpty() &&
                            name.compare( "Content-Transfer-Encoding",Qt::CaseInsensitive ) == 0 )
						{
							// Getting local Content-Transfer-Encoding
							if ( temp.startsWith( '"' ) && temp.endsWith( '"' ) )
							{
								temp.remove( 0, 1 );
								temp.remove( temp.size() - 1, 1 );
							}

                            encoding= temp ;
						}
					} // end treat parameter

                    temp= line ;
				}
				// Parse Header
                if ( line ==  localBoundary )
				{
					if ( extractedText.isEmpty() && e_text == enumConType_ )
					{
						bMulti = false;
						bAddAttachment_ = false;
					}

					addAttachment();

                    if ( localBoundary == boundary )
					{
						boundaryPos = curLinePos;
						temp.clear();
						localBoundary.clear();
						continue;
					}
					else
					{
						// Next is parsing of body
						bHeader = false;
					}
				}
				else
				{
                    if ( line == endBoundary )
					{
						if ( extractedText.isEmpty() && e_text == enumConType_ )
						{
							bMulti = false;
							bAddAttachment_ = false;
						}

						addAttachment();

						boundaryPos = curLinePos;

						// End of part
						if ( boundaryStack.isEmpty() ) return true;

						bHeader = true;
						bMulti = false;

						// Exit from nested part
                        localBoundary = boundaryStack.top();
                        boundary = localBoundary ;
                        endBoundary = boundary ;
                        endBoundary.append("--");

						boundaryStack.pop();
					}
                    else if ( line == boundary )
					{
						if ( extractedText.isEmpty() && e_text == enumConType_ )
						{
							bMulti = false;
							bAddAttachment_ = false;
						}

						addAttachment();

						boundaryPos = curLinePos;
					}
				}
			}
			else
			{
				// Parse Body
                if ( line == endBoundary )
				{
					boundaryPos = curLinePos;
					if ( boundaryStack.isEmpty() ) 
					{
						Result = true;
						break;
					}

					bHeader = true;
					bMulti = false;

                    localBoundary = boundaryStack.top();
                    boundary = localBoundary;
                    endBoundary = boundary ;
                    endBoundary.append("--");

					boundaryStack.pop();
				}
                else if ( line == boundary )
				{
					boundaryPos = curLinePos;
					bHeader = true;
					bMulti = false;
                    temp = emailStream_->readLine();

                    if ( emailStream_->atEnd() )
					{
						// No end boundary
						Result = false;
						break;
					}
				}
				else
				{
					if ( e_text == enumConType_ && !bMulti )	// it's may be Content-Type: text/plain 
																// and attachment at one time
					{
						// Check for UUEncoded message
						if ( line.startsWith( "begin " ) )
						{
							QByteArray filename;
							if ( isUUencoded( line, filename ) )
							{
								attachments_.push_back( 
									AttachmentItem( QString::fromUtf8( filename ), curLinePos ) );
								QByteArray temp;
								uudecode( false, temp );
							}
						}
						else
						{
                            extractedText.append( line );
							if ( !line.endsWith( '\n' ) && !extractedText.endsWith( "\r\n\r\n" ) )
							{
								extractedText.append( "\r\n" );
							}
						}
					}
				}
			} // Parsing of body
 		} // while
    }
	else if ( e_text == enumConType_ )
	{
        while ( !emailStream_->atEnd() )
		{
            int curLinePos = emailStream_->pos();
            line = emailStream_->readLine();
			// Check for UUEncoded message
			if ( line.startsWith( "begin " ) )
			{
				QByteArray filename;
				if ( isUUencoded( line, filename ) )
				{
					attachments_.push_back( 
						AttachmentItem( QString::fromUtf8( filename ), curLinePos ) );
					QByteArray temp;
					uudecode( false, temp );
				}
			}
			else
			{
                extractedText.append( line );
				if ( !line.endsWith( '\n' ) && !extractedText.endsWith( "\r\n\r\n" ) )
				{
					extractedText.append( "\r\n" );
				}
			}
		}
	}

	encode( extractedText, encoding );
    QTextCodec *codec = QTextCodec::codecForName( charset.toLatin1() );
	if ( codec )
	{
		text_ = codec->toUnicode( extractedText );
	}
	else
	{
		codec = QTextCodec::codecForLocale();
		if ( codec )
		{
			text_ = codec->toUnicode( extractedText );
        }
		else
		{
			text_ = QString::fromLocal8Bit( extractedText );
		}
	}

	return Result;
}

//	============================================================
//	EmailMessage::decodeNonAsciiText
///
/// Decode message header parameter text that has rfc1522 
/// ( non Ascii ) format

QString EmailMessage::decodeNonAsciiText( QString &text )
{
	int nStart = 0, nPoser = 0;
	QByteArray strResult;
    bool bShortForm = false;
    QString retCharset;
    QString temp;
    QString temp1;
    QString strEncoding;
	while ( -1 != ( nStart = text.indexOf( "=?" ) ) )
	{
		bShortForm = true;
		QByteArray strTemp;

		// Getting parts of encoded data
		// If string has non coded pieces, add them to result string
		if ( nStart != 0 )
		{
            temp = text.mid( 0, nStart );
            strResult += temp;
		}

		text.remove( 0, nStart + 2 );

		if ( -1 == ( nPoser = text.indexOf( '?' ) ) ) return "";

        retCharset = text.mid( 0, nPoser );
        //retCharset.assign( text );
		text.remove( 0, nPoser + 1 );

		if ( -1 == ( nPoser = text.indexOf( '?' ) ) ||  nPoser != 1 ) return "";

		// Check encoding
		if ( 'Q' == text.at( 0 ) || 'q' == text.at( 0 ) )
		{
            strEncoding = "quoted-printable";//strEncoding.assign( "quoted-printable", sizeof( "quoted-printable" ) - 1 );
		}
		else if ( 'B' == text.at( 0 ) || 'b' == text.at( 0 ) )
		{
            strEncoding = "base64";//strEncoding.assign( "base64", sizeof( "base64" ) - 1 );
		}
		else 
		{
			text.remove( 0, 2 );

            temp1 = strResult;//temp1.assign( strResult.data(), strResult.size() );
            temp1.append(text);//temp1.add( text );
            text = temp1;

			return "";
		}

		text.remove( 0, 2 );

		if ( -1 != ( nPoser = text.indexOf( "?=" ) ) )
		{
            temp1 = text.mid( 0, nPoser );
            strTemp = temp1.toLatin1();
			text.remove( 0, nPoser + 2 );
		}
        else return "";

		encode( strTemp, strEncoding, bShortForm );

		strResult += strTemp;
	}

    if ( !text.isEmpty() )
    {
        strResult += text;
    }
	// Final Result
    text = strResult;

    QString finalString;
    QTextCodec *codec = QTextCodec::codecForName( retCharset.toLatin1() );
	if ( codec )
	{
        finalString = codec->toUnicode( text.toLatin1() );
	}
	else
	{
		codec = QTextCodec::codecForLocale();
		if ( codec )
		{
            finalString = codec->toUnicode( text.toLatin1() );
		}
		else
		{
            finalString = QString::fromLocal8Bit( text.toLatin1());
		}
	}
	return finalString;
}

//	============================================================
//	EmailMessage::encode
///
/// Decode the text if encoding is quoted-printable or Base64

int EmailMessage::encode( QByteArray &text, const QString &encoding, bool bShortForm )
{
	int result = 0;
	QByteArray strLine;

    if ( encoding.compare( "quoted-printable",Qt::CaseInsensitive )  == 0)
	{
		// Quoted-printable encoding
		int nPos;

		while ( -1 != ( nPos = text.indexOf( "=\r\n" ) ) )
		{
			text.remove( nPos, 3 );
		}

		// Replace substrings
		char nCode;
		int i = 0;

		while ( i < text.length() )
		{
			if ( '=' == text[ i ] )
			{
				if ( isHexString( text.mid( i + 1, 2 ) ) )
				{
					// Replace some symbol
					nCode = HexCode( text[ i + 1 ] )*16 + HexCode( text[ i + 2 ] );
					text.remove( i, 3 );
					char c[ 2 ] = { nCode, '\0' };
					text.insert( i, ( char* ) c );
				}
				else
				{
					text.remove( i, 1 );
					if ( i > 0 ) i--;
				}
			}
			else if ( bShortForm && '_' == text[ i ] )
			{
				text[ i ] = ' ';
			}
			i++;
		}
	}
    else if ( encoding.compare("base64",Qt::CaseInsensitive )  == 0)
	{
		// Base64 encoding
		text = QByteArray::fromBase64( text );
	}
	else
	{
		result = 1;
	}

	return result;
}

//	============================================================
//	EmailMessage::isHexString

inline bool EmailMessage::isHexString( const QByteArray &hex )
{
	for ( int i = 0; i < hex.length(); i++ )
	{
		if ( !(( hex[ i ] >= '0' && hex[ i ] <= '9' ) ||
			( hex[ i ] >= 'a' && hex[ i ] <= 'f' ) ||
			( hex[ i ] >= 'A' && hex[ i ] <= 'F' )) )
		{
			return false;
		}
	}

	return true;
}

//	============================================================
//	EmailMessage::parseAddrList

void EmailMessage::parseAddrList( QStringList &addrList, QString &paramText )
{
    int lMark, rMark;
    QStringList  splitAddress = paramText.split(',',QString::SkipEmptyParts);
    for( QStringList::iterator iter = splitAddress.begin();iter != splitAddress.end();++iter)
    {
        lMark = iter->indexOf( '<' );
        rMark = iter->indexOf( '>' );

        if ( -1 != lMark && -1 != lMark && rMark > lMark )
        {
            lMark += 1;
            QByteArray email = iter->toLatin1();
            email = email.mid( lMark, rMark - lMark );

            iter->remove( lMark - 1,  iter->size() - lMark + 1 );
            (*iter)=iter->trimmed();

            if ( !iter->isEmpty() )
            {
                // If name enclose in '"'
                if ( iter->startsWith( '"' ) && iter->endsWith( '"' ) )
                {
                    iter->remove( 0, 1 );
                    iter->remove( iter->size() - 1, 1 );
                }

                QString toName = decodeNonAsciiText( *iter );
                addrList.append( toName + " <" + email + ">" );
            }
            else
            {
                addrList.append( email );
            }
        }
        else
        {
            // If it has '@' then accept that as email otherwise accept as name
            if ( -1 != ( iter->indexOf( '@' ) ) )
            {
                addrList.append(*iter);
            }
            else
            {
                (*iter)=iter->trimmed();
                if ( iter->isEmpty() )
                {
                    addrList.append( decodeNonAsciiText( *iter ) );
                }
            }
        }
    }
}

//	============================================================
//	EmailMessage::getExt

void EmailMessage::getExt( const QByteArray &contentType, QByteArray &extension )
{
    int pos = contentType.indexOf( ';' );
	if ( -1 == pos ) pos = contentType.size();
	QByteArray subType = contentType.left( pos );

	// Default ext
	extension = "txt";

	// Get appropriate ext
	if ( subType.startsWith( "text/" ) )
	{
		if ( subType == constTextPlain )
		{
			extension = "txt";
		}
		else
		{
			extension = subType.right( subType.length() - sizeof( "text/" ) + 1 );
		}
	}
    else if (constRfc822. compare( subType,Qt::CaseInsensitive ) == 0)
    {
		extension = "msg";
	}
	else if ( subType.startsWith( "image/" ) )
	{
		extension = subType.right( subType.length() - sizeof( "image/" ) + 1 );
	}
}

//	============================================================
//	EmailMessage::parseContentType

void EmailMessage::parseContentType(QString &paramText )
{
    bool bFirst = true;
    int nMark = 0;
    QString name;
    QStringList tokenList = paramText.split(";",QString::SkipEmptyParts);
    for( QStringList::iterator iter = tokenList.begin();iter != tokenList.end();++iter)
    {
        // First token determine type of parameter Content-Type
        // and for this is using enumerator enumConType_
        if ( bFirst )
        {
            // Parsing first subparameter : type / subtype
            contentType_ = iter->toLatin1();
            if ( -1 != ( nMark =   iter->indexOf( '/' ) ) )
            {
                // Expect an error of several Content-Types in main Header
                name = iter->mid( 0, nMark );
                // Determining the type of Content-Type
                if ( name.compare( constMultipart ,Qt::CaseInsensitive)  == 0)
                {
                    enumConType_ = e_multipart;
                }
                else if ( (iter->compare( constTextPlain,Qt::CaseInsensitive) == 0) ||
                     (iter->compare( constTextHtml ,Qt::CaseInsensitive ) == 0) ||
                     (iter->compare( constRfc822 ,Qt::CaseInsensitive )  == 0) )
                {
                    enumConType_ = e_text;
                }
                else
                {
                    enumConType_ = e_other;
                }
            }
            else
            {
                // Bad format of Content-Type and as default
                // accept text/plain
                enumConType_ = e_text;
            }

            bFirst = false;
        }
        else
        {
            // Parsing other subparameters
            if ( -1 != ( nMark = iter->indexOf( '=' ) ) )
            {
                name = iter->mid( 0, nMark ).trimmed();

                 iter->remove( 0, nMark + 1 );
                 (*iter)=iter->trimmed();

                // Deleting enclosed quotation marks
                if (   iter->startsWith( '"' ) &&  iter->endsWith( '"' ) )
                {
                    iter->remove( 0, 1 );
                     iter->remove(  iter->size() - 1, 1 );
                }

                if ( e_multipart == enumConType_ )
                {
                    if ( name.compare( constBoundary,Qt::CaseInsensitive )  == 0)
                    {
                        boundary_ = QByteArray( "--" ) +   iter->toLatin1();
                    }
                }

                if ( name.compare( constCharset,Qt::CaseInsensitive ) == 0 )
                {
                    charset_ =  iter->toLatin1();
                }
            } // if
        } // else
    } // while
}

//	============================================================
//	EmailMessage::parseFrom

void EmailMessage::parseFrom( QString &paramText )
{
	int lMark, rMark;
//    QString address;
	lMark = paramText.indexOf( '<' );
	rMark = paramText.indexOf( '>' );

	if ( -1 != lMark && -1 != lMark && rMark > lMark )
	{
		lMark += 1;
//        address= paramText.mid( lMark, rMark - lMark );
//        paramText.remove( lMark - 1, paramText.size() - lMark + 1 );
//        paramText = paramText.trimmed();

//		if ( !paramText.isEmpty() )
//		{
//			// If name enclosed in '"'
//			if ( paramText.startsWith( '"' ) && paramText.endsWith( '"' ) )
//			{
//				paramText.remove( 0, 1 );
//				paramText.remove( paramText.size() - 1, 1 );
//			}
//		}
//		sender_ = decodeNonAsciiText( paramText );
//        sender_ += QByteArray( " <" ) + address+ ">";
        sender_ = paramText.mid( lMark, rMark - lMark );
	}
	else
	{
        sender_ = paramText;
	}
}

//	============================================================
//	EmailMessage::parseDate

void EmailMessage::parseDate( QString &paramText )
{
	// Parse date which has format like following:
	// Tue, 1 Aug 2006 10:30:34 +0300

	// Delete weekday
	int comma = 0;
	if ( ( comma = paramText.indexOf( ',' ) ) != -1 )
	{
		paramText.remove( 0, comma + 1 );
	}
    // Delete timezone as all after time
	int pos = paramText.lastIndexOf( ':' );
	pos = paramText.indexOf( ' ', pos );

	if ( -1 != pos )
	{
		paramText.remove( pos, paramText.size() - pos );
	}
    paramText = paramText.trimmed();
    QLocale locale(QLocale::C);
     date_ = locale.toDateTime( paramText, "d MMM yyyy hh:mm:ss" );
}

//	============================================================
//	EmailMessage::parseContentDisp

void EmailMessage::parseContentDisp( QString &paramText, bool &bMulti,
	bool bTextEmpty, int boundaryPos, int &nameCounter )
{
	bool bFirst = true;
	bMulti = false;
	attachName_ = "";
	attachPos_ = 0;
	bAddAttachment_ = false;
    QString sub;
    QStringList name = paramText.split(";",QString::SkipEmptyParts);
	int nMark = 0;
    for( QStringList::iterator iter = name.begin();iter != name.end();++iter)
    {
        (*iter) = iter->trimmed();

		if ( bFirst )
		{
			// An attachment if
            if ( iter->compare( "attachment" ,Qt::CaseInsensitive) == 0)
			{
				bMulti = true;
			}
            else if ( iter->compare( "inline" ,Qt::CaseInsensitive) == 0 )
			{
				bMulti = true;
			}
			bFirst = false;
		}
		else if ( bMulti )
		{
            if ( -1 != ( nMark = iter->indexOf( '=' ) ) )
			{
                sub = iter->mid( 0, nMark ).trimmed();
                iter->remove( 0, nMark + 1 );
                (*iter) = iter->trimmed();

				// Deleting enclosed quotation marks
                if ( iter->startsWith( '"' ) && iter->endsWith( '"' ) )
				{
                    iter->remove( 0, 1 );
                    iter->remove( iter->size() - 1, 1 );
				}
                if ( sub.compare( "filename",Qt::CaseInsensitive ) == 0)
                {
					// Name of attachment
                    attachName_ = decodeNonAsciiText( *iter );
					bAddAttachment_ = true;
					attachPos_ = boundaryPos;
				}
			}
		}
	} // while tokenizer has more tokens

	if ( bMulti && !bAddAttachment_ )
	{
		if ( e_text == enumConType_ && bTextEmpty )
		{
			bMulti = false;
		}
		else
		{
			QByteArray extension;
			getExt( contentType_, extension );

			// Check for empty attachment name
			nameCounter++;
			attachName_ = QString::number( nameCounter ) + "." + extension;
			bAddAttachment_ = true;
			attachPos_ = boundaryPos;
		}
	}
}

//	============================================================
//	EmailMessage::addAttachment

void EmailMessage::addAttachment()
{
	if ( bAddAttachment_ )
	{
		attachments_.push_back( AttachmentItem( attachName_, attachPos_ ) );
		bAddAttachment_ = false;
	}
}

//	============================================================
//	EmailMessage::parseHeaderOnly

void EmailMessage::parseHeaderOnly( bool bOnly )
{
	bParseHeaderOnly_ = bOnly;
}

//	============================================================
//	EmailMessage::isUUencoded

bool EmailMessage::isUUencoded( QString &line, QByteArray &filename )
{
	QTextCodec *codec = QTextCodec::codecForLocale();
	QString uline;

	if ( codec )
	{
        uline = codec->toUnicode( line.toLatin1() );
	}
	else
	{
        uline = QString::fromUtf8( line.toLatin1() );
	}

	QStringList tokens = uline.split( ' ', QString::SkipEmptyParts );

	if ( tokens.size() != 3 )
	{
		return false;
	}

	for ( int i = 0; i < tokens.at( 1 ).size(); i++ )
	{
		if ( !tokens.at( 1 ).at( i ).isDigit() )
		{
			return false;
		}
	}

	filename = tokens.at( 2 ).trimmed().toUtf8();
	return true;
}

//	============================================================
//	EmailMessage::uudecode

void EmailMessage::uudecode( bool extract, QByteArray &outData )
{
#define DEC(c)  (((c) - ' ') & 077)
	if ( extract )
	{
		outData.clear();
	}
    QString bufstr;
	register int n = 0;
	register char ch = 0, *p = 0;

    while ( !emailStream_->atEnd() )
	{
        bufstr = emailStream_->readLine();
		if ( bufstr.startsWith( "end" ) )
		{
			break;
		}

		if ( extract )
		{
            QByteArray bufArray = bufstr.toLatin1();
            p = bufArray.data();
            //p = buf;
			n = DEC (*p);

			if (n <= 0)
			break;

			for ( ++p; n > 0; p += 4, n -= 3 )
			{
				if ( n >= 3 )
				{
					ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4; 
					outData.append( ch );
					ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2; 
					outData.append( ch );
					ch = DEC (p[2]) << 6 | DEC (p[3]);
					outData.append( ch );
				}
				else
				{
					if (n >= 1)
					{
						ch = DEC (p[0]) << 2 | DEC (p[1]) >> 4; 
						outData.append( ch );
					}
					if (n >= 2)
					{
						ch = DEC (p[1]) << 4 | DEC (p[2]) >> 2; 
						outData.append( ch );
					}
				}
			}
		}
	}
}
